#include "htu21d_i2c.h"

void i2c_init()
{
	I2C_SDA_OUT();
	I2C_SCL_OUT();
	I2C_SDA_HIGH();
	I2C_SCL_HIGH();
	I2C_DELAY_US(20000);
}

void i2c_start()
{
	I2C_SDA_OUT();

	I2C_SDA_HIGH();
	I2C_SCL_HIGH();
	I2C_DELAY_US(4);

	I2C_SDA_LOW();
	I2C_DELAY_US(4);
	I2C_SCL_LOW();
	I2C_DELAY_US(4);
}

void i2c_stop()
{
	I2C_SDA_OUT();

	I2C_SDA_LOW();
	I2C_SCL_LOW();
	I2C_DELAY_US(4);

	I2C_SCL_HIGH();
	I2C_DELAY_US(4);
	I2C_SDA_HIGH();
	I2C_DELAY_US(4);
}

void i2c_ack()
{
	I2C_SDA_OUT();

	I2C_SCL_LOW();
	I2C_DELAY_US(4);
	I2C_SDA_HIGH();
	I2C_DELAY_US(4);

	I2C_SDA_LOW();
	I2C_DELAY_US(4);
	I2C_SCL_HIGH();
	I2C_DELAY_US(4);
	I2C_SCL_LOW();
	I2C_DELAY_US(4);
	I2C_SDA_HIGH();
}

void i2c_nack()
{
	I2C_SDA_OUT();

	I2C_SCL_LOW();
	I2C_DELAY_US(4);
	I2C_SDA_HIGH();
	I2C_DELAY_US(4);

	I2C_SCL_HIGH();
	I2C_DELAY_US(4);
	I2C_SCL_LOW();
	I2C_DELAY_US(4);
}

bool i2c_wait_ack()
{
	I2C_SCL_LOW();
	I2C_SDA_HIGH();
	I2C_DELAY_US(4);
	I2C_SDA_IN();
	I2C_SCL_HIGH();
	I2C_DELAY_US(4);

	int timeout = 0;
	while (I2C_SDA_READ())
	{
		timeout++;
		if (timeout >= 250)
		{
			i2c_stop();
			return false;
		}
	}
	I2C_SCL_LOW();
	I2C_DELAY_US(4);
	I2C_SDA_OUT();
	I2C_SDA_HIGH();
	return true;
}

void i2c_read(uint8_t *byte)
{
	I2C_SCL_LOW();
	I2C_DELAY_US(4);
	I2C_SDA_HIGH();
	I2C_SDA_IN();
	int i = 8, b = 0;
	for (; i > 0; i--)
	{
		b = b<<1;
		I2C_SCL_HIGH();
		I2C_DELAY_US(4);
		if (I2C_SDA_READ())
		{
			b++;
		}
		I2C_SCL_LOW();
		I2C_DELAY_US(4);
	}
	*byte = b;
}

void i2c_write(uint8_t byte)
{
	I2C_SDA_OUT();
	I2C_SCL_LOW();
	int i = 7;
	for (; i >= 0; i--)
	{
		if ((byte>>i) & 0x01) {
			I2C_SDA_HIGH();
		} else {
			I2C_SDA_LOW();
		}
		I2C_DELAY_US(4);
		I2C_SCL_HIGH();
		I2C_DELAY_US(4);
		I2C_SCL_LOW();
		I2C_DELAY_US(4);
	}
	I2C_DELAY_US(6);
}

int htu21d_reset()
{
	i2c_start();
	i2c_write(HTU21D_ADDR);
	if (!i2c_wait_ack()) return 1;
	i2c_write(HTU21D_REG_RESET);
	if (!i2c_wait_ack()) return 2;
	i2c_stop();

	I2C_DELAY_US(20000);

	return 0;
}

typedef uint8_t uint8;
typedef uint32_t uint32;
bool HTU21D_CRC(uint8 msb, uint8 lsb, uint8 crc) {
	uint32 value = msb<<16 | lsb <<8 | crc;
	//polynomial = x^8 + x^5 + x^4 + 1 = 0b000000000000000100110001 = 0x000131
	//operating on 3 byte value of MSB|LSB|CRC
	//0x000131<<27 = 0b100110001000000000000000 = 0x988000 shifted left to align on 3byte boundary
	uint32 divisor = 0x988000;

	uint8 i;
	for(i=0; i < 16; i++){
		if(value & 1<<(23-i)){
			value ^= divisor;
		}
		divisor >>= 1;
	}
	if(value) return 0; //remainder of value should be 0 otherwise CRC check failed.

	return 1; //CRC check OK
}

int htu21d_read_temperature(int16_t *temperature_mC)
{
	uint8_t msb = 0, lsb = 0, checksum = 0;
	*temperature_mC = 0;

	i2c_start();
	i2c_write(HTU21D_ADDR);
	if (!i2c_wait_ack()) return 1;

	i2c_write(HTU21D_REG_TEMP);
	if (!i2c_wait_ack()) return 6;
	I2C_DELAY_US(50000);  // 等待测量完成

	i2c_start();
	i2c_write(HTU21D_ADDR + 1);
	if (!i2c_wait_ack()) return 2;

	i2c_read(&msb);
	i2c_ack();
	//I2C_DELAY_US(4);
	i2c_read(&lsb);
	i2c_ack();
	//I2C_DELAY_US(4);
	i2c_read(&checksum);
	i2c_nack();
	//I2C_DELAY_US(4);
	i2c_stop();

	I2C_DEBUG_PRINT("msb: 0x%02x, lsb: 0x%02x", msb, lsb);

	uint16_t temp = 0;
	temp = lsb | (msb << 8);

	I2C_DEBUG_PRINT("crc check: %s", HTU21D_CRC(msb, lsb, checksum) ? "success" : "failed");

	int16_t t = 0;
	t = -4685 + 17572 * temp / 65535;
	*temperature_mC = t;

	char buffer[64] = {0};
	sprintf(buffer, "%d.%d", t / 100, t %100);

	I2C_DEBUG_PRINT("temperature reg value: %d,  checksum: %d", temp, checksum);
	I2C_DEBUG_PRINT("temperature: %s", buffer);
	return 0;
}

int htu21d_read_humidity(int16_t *humidity_mP)
{
	uint8_t msb = 0, lsb = 0, checksum = 0;
	*humidity_mP = 0;

	i2c_start();
	i2c_write(HTU21D_ADDR);
	if (!i2c_wait_ack()) return 1;

	i2c_write(HTU21D_REG_HUMI);
	if (!i2c_wait_ack()) return 6;
	I2C_DELAY_US(30000); //等待测量完成

	i2c_start();
	i2c_write(HTU21D_ADDR + 1);
	if (!i2c_wait_ack()) return 2;

	i2c_read(&msb);
	i2c_ack();
	//I2C_DELAY_US(4);
	i2c_read(&lsb);
	i2c_ack();
	//I2C_DELAY_US(4);
	i2c_read(&checksum);
	i2c_nack();
	//I2C_DELAY_US(4);
	i2c_stop();

	I2C_DEBUG_PRINT("msb: 0x%02x, lsb: 0x%02x", msb, lsb);
	I2C_DEBUG_PRINT("crc check: %s", HTU21D_CRC(msb, lsb, checksum) ? "success" : "failed");

	uint16_t humi = 0;
	humi = lsb | (msb << 8);

	int16_t t = 0;
	t = -600 + 12500 * humi / 65535;
	*humidity_mP = t;

	char buffer[64] = {0};
	sprintf(buffer, "%d.%d%%", t / 100, t %100);

	I2C_DEBUG_PRINT("humidity reg value: %d,  checksum: %d", humi, checksum);
	I2C_DEBUG_PRINT("humidity: %s", buffer);
	return 0;
}
